/******************************************************************************
 *
 * MantaFlow fluid solver framework
 * Copyright 2011 Tobias Pfaff, Nils Thuerey 
 *
 * This program is free software, distributed under the terms of the
 * Apache License, Version 2.0 
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Vec3 class extension for python
 *
 ******************************************************************************/

#include "pythonInclude.h"
#include <string>
#include <sstream>
#include "vectorbase.h"
#include "structmember.h"
#include "manta.h"

using namespace std;

namespace Manta {
  
extern PyTypeObject PbVec3Type;

struct PbVec3 {
	PyObject_HEAD
	float data[3];
};

static void PbVec3Dealloc(PbVec3* self) {
	Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject * PbVec3New(PyTypeObject *type, PyObject *args, PyObject *kwds) {    
	return type->tp_alloc(type, 0);
}

static int PbVec3Init(PbVec3 *self, PyObject *args, PyObject *kwds) {
	
	float x1 = numeric_limits<float>::quiet_NaN(), x2=x1, x3=x1;
	if (!PyArg_ParseTuple(args,"|fff",&x1, &x2, &x3))
		return -1;
	
	if (!c_isnan(x1)) {
		self->data[0] = x1;
		if (!c_isnan(x2) && !c_isnan(x3)) {
			self->data[1] = x2;
			self->data[2] = x3;
		} else {
			if (!c_isnan(x2) || !c_isnan(x3)) { errMsg("Invalid partial init of vec3"); }
			self->data[1] = x1;
			self->data[2] = x1;
		}
	} else {
		self->data[0] = 0;
		self->data[1] = 0;
		self->data[2] = 0;
	}
	return 0;
}

static PyObject* PbVec3Repr(PbVec3* self) {
	Manta::Vec3 v(self->data[0], self->data[1], self->data[2]);
	return PyUnicode_FromFormat(v.toString().c_str());
}

static PyMemberDef PbVec3Members[] = {
	{(char*)"x", T_FLOAT, offsetof(PbVec3, data), 0, (char*)"X"},
	{(char*)"y", T_FLOAT, offsetof(PbVec3, data)+sizeof(float), 0, (char*)"Y"},
	{(char*)"z", T_FLOAT, offsetof(PbVec3, data)+sizeof(float)*2, 0, (char*)"Z"},
	{NULL}  // Sentinel
};

static PyMethodDef PbVec3Methods[] = {
	//{"name", (PyCFunction)Noddy_name, METH_NOARGS, "Return the name, combining the first and last name" },
	{NULL}  // Sentinel
};

// operator overloads
	
inline PyObject* PbNew(const Vec3& a) { 
	PbVec3* obj = (PbVec3*)PbVec3New(&PbVec3Type,0,0); 
	obj->data[0] = a.x;
	obj->data[1] = a.y;
	obj->data[2] = a.z;
	return (PyObject*)obj;
}

#define CONVERTVEC(obj) \
	Vec3 v##obj; \
	if (PyObject_TypeCheck(obj, &PbVec3Type)) \
		v##obj = Vec3(&( ((PbVec3*)obj)->data[0])); \
	else if (PyFloat_Check(obj)) \
		v##obj = Vec3(PyFloat_AsDouble(obj)); \
	else if (PyLong_Check(obj)) \
		v##obj = Vec3(PyLong_AsDouble(obj)); \
	else { \
		Py_INCREF(Py_NotImplemented); \
		return Py_NotImplemented; \
	} \

#define OPHEADER \
	if (!PyObject_TypeCheck(a, &PbVec3Type) && !PyObject_TypeCheck(b, &PbVec3Type)) { \
		Py_INCREF(Py_NotImplemented); \
		return Py_NotImplemented; \
	} \
	CONVERTVEC(a) \
	CONVERTVEC(b)

#define OPHEADER1 \
	if (!PyObject_TypeCheck(a, &PbVec3Type)) { \
		Py_INCREF(Py_NotImplemented); \
		return Py_NotImplemented; \
	} \
	CONVERTVEC(a)
	
PyObject* PbVec3Add(PyObject* a, PyObject* b) {
	OPHEADER
	return PbNew(va+vb);
}

PyObject* PbVec3Sub(PyObject* a, PyObject* b) {
	OPHEADER
	return PbNew(va-vb);
}

PyObject* PbVec3Mult(PyObject* a, PyObject* b) {
	OPHEADER
	return PbNew(va*vb);
}

PyObject* PbVec3Div(PyObject* a, PyObject* b) {
	OPHEADER
	return PbNew(va/vb);
}

PyObject* PbVec3Negative(PyObject* a) {
	OPHEADER1
	return PbNew(-va);
}

// numbers are defined subtely different in Py3 (WTF?)
#if PY_MAJOR_VERSION >= 3
static PyNumberMethods PbVec3NumberMethods = {
	 (binaryfunc)PbVec3Add,          // binaryfunc nb_add;
	 (binaryfunc)PbVec3Sub,          // binaryfunc nb_sub;
	 (binaryfunc)PbVec3Mult,         // binaryfunc nb_mult;
	 0,          // binaryfunc nb_remainder;
	 0,          // binaryfunc nb_divmod;
	 0,          // ternaryfunc nb_power;
	 (unaryfunc)PbVec3Negative,          // unaryfunc nb_negative;
	 0,          // unaryfunc nb_positive;
	 0,          // unaryfunc nb_absolute;
	 0,          // inquiry nb_bool;
	 0,          // unaryfunc nb_invert;
	 0,          // binaryfunc nb_lshift;
	 0,          // binaryfunc nb_rshift;
	 0,          // binaryfunc nb_and;
	 0,          // binaryfunc nb_xor;
	 0,          // binaryfunc nb_or;
	 0,          // unaryfunc nb_int;
	 0,          // void *nb_reserved;
	 0,          // unaryfunc nb_float;
	 0,          // binaryfunc nb_inplace_add;
	 0,          // binaryfunc nb_inplace_subtract;
	 0,          // binaryfunc nb_inplace_multiply;
	 0,          // binaryfunc nb_inplace_remainder;
	 0,          // ternaryfunc nb_inplace_power;
	 0,          // binaryfunc nb_inplace_lshift;
	 0,          // binaryfunc nb_inplace_rshift;
	 0,          // binaryfunc nb_inplace_and;
	 0,          // binaryfunc nb_inplace_xor;
	 0,          // binaryfunc nb_inplace_or;

	 0,          // binaryfunc nb_floor_divide;
	 (binaryfunc)PbVec3Div,          // binaryfunc nb_true_divide;
	 0,          // binaryfunc nb_inplace_floor_divide;
	 0,          // binaryfunc nb_inplace_true_divide;

	 0           // unaryfunc nb_index;
};
#else
static PyNumberMethods PbVec3NumberMethods = {
	 (binaryfunc)PbVec3Add,          // binaryfunc nb_add;
	 (binaryfunc)PbVec3Sub,          // binaryfunc nb_sub;
	 (binaryfunc)PbVec3Mult,         // binaryfunc nb_mult;
	0,                               // binaryfunc nb_divide;
	0,                               // binaryfunc nb_remainder;
	0,                               // binaryfunc nb_divmod;
	0,                               // ternaryfunc nb_power;
	(unaryfunc)PbVec3Negative,       // unaryfunc nb_negative;
	0,                               // unaryfunc nb_positive;
	0,                               // unaryfunc nb_absolute;
	0,                               // inquiry nb_nonzero;
	0,                               // unaryfunc nb_invert;
	0,                               // binaryfunc nb_lshift;
	0,                               // binaryfunc nb_rshift;
	0,                               // binaryfunc nb_and;
	0,                               // binaryfunc nb_xor;
	0,                               // binaryfunc nb_or;
	0,                               // coercion nb_coerce;
	0,                               // unaryfunc nb_int;
	0,                               // unaryfunc nb_long;
	0,                               // unaryfunc nb_float;
	0,                               // unaryfunc nb_oct;
	0,                               // unaryfunc nb_hex;
	0,                               // binaryfunc nb_inplace_add;
	0,                               // binaryfunc nb_inplace_subtract;
	0,                               // binaryfunc nb_inplace_multiply;
	0,                               // binaryfunc nb_inplace_divide;
	0,                               // binaryfunc nb_inplace_remainder;
	0,                               // ternaryfunc nb_inplace_power;
	0,                               // binaryfunc nb_inplace_lshift;
	0,                               // binaryfunc nb_inplace_rshift;
	0,                               // binaryfunc nb_inplace_and;
	0,                               // binaryfunc nb_inplace_xor;
	0,                               // binaryfunc nb_inplace_or;
	0,                               // binaryfunc nb_floor_divide;
	(binaryfunc)PbVec3Div,           // binaryfunc nb_true_divide;
	0,                               // binaryfunc nb_inplace_floor_divide;
	0,                               // binaryfunc nb_inplace_true_divide;
	0,                               // unaryfunc nb_index;
};
#endif

PyTypeObject PbVec3Type = {
	PyVarObject_HEAD_INIT(NULL, 0)
	"manta.vec3",             /* tp_name */
	sizeof(PbVec3),             /* tp_basicsize */
	0,                         /* tp_itemsize */
	(destructor)PbVec3Dealloc, /* tp_dealloc */
	0,                         /* tp_print */
	0,                         /* tp_getattr */
	0,                         /* tp_setattr */
	0,                         /* tp_reserved */
	(reprfunc)PbVec3Repr,      /* tp_repr */
	&PbVec3NumberMethods,      /* tp_as_number */
	0,                         /* tp_as_sequence */
	0,                         /* tp_as_mapping */
	0,                         /* tp_hash  */
	0,                         /* tp_call */
	0,                         /* tp_str */
	0,                         /* tp_getattro */
	0,                         /* tp_setattro */
	0,                         /* tp_as_buffer */
#if PY_MAJOR_VERSION >= 3
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE ,   /* tp_flags */
#else
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |  Py_TPFLAGS_CHECKTYPES,   /* tp_flags */
#endif
	"float vector type",           /* tp_doc */
	0,                     /* tp_traverse */
	0,                     /* tp_clear */
	0,                     /* tp_richcompare */
	0,                     /* tp_weaklistoffset */
	0,                     /* tp_iter */
	0,                     /* tp_iternext */
	PbVec3Methods,             /* tp_methods */
	PbVec3Members,             /* tp_members */
	0,                         /* tp_getset */
	0,                         /* tp_base */
	0,                         /* tp_dict */
	0,                         /* tp_descr_get */
	0,                         /* tp_descr_set */
	0,                         /* tp_dictoffset */
	(initproc)PbVec3Init,      /* tp_init */
	0,                         /* tp_alloc */
	PbVec3New,                 /* tp_new */
};

inline PyObject* castPy(PyTypeObject* p) { 
	return reinterpret_cast<PyObject*>(static_cast<void*>(p)); 
}


// 4d vector

extern PyTypeObject PbVec4Type;

struct PbVec4 {
	PyObject_HEAD
	float data[4];
};

static PyMethodDef PbVec4Methods[] = {
	{NULL}  // Sentinel
};

static PyMemberDef PbVec4Members[] = {
	{(char*)"x", T_FLOAT, offsetof(PbVec4, data), 0, (char*)"X"},
	{(char*)"y", T_FLOAT, offsetof(PbVec4, data)+sizeof(float)*1, 0, (char*)"Y"},
	{(char*)"z", T_FLOAT, offsetof(PbVec4, data)+sizeof(float)*2, 0, (char*)"Z"},
	{(char*)"t", T_FLOAT, offsetof(PbVec4, data)+sizeof(float)*3, 0, (char*)"T"},
	{NULL}  // Sentinel
};


static void PbVec4Dealloc(PbVec4* self) {
	Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject * PbVec4New(PyTypeObject *type, PyObject *args, PyObject *kwds) {    
	return type->tp_alloc(type, 0);
}

static int PbVec4Init(PbVec4 *self, PyObject *args, PyObject *kwds) {
	
	float x1 = numeric_limits<float>::quiet_NaN(), x2=x1, x3=x1, x4=x1;
	if (!PyArg_ParseTuple(args,"|ffff",&x1, &x2, &x3, &x4))
		return -1;
	
	if (!c_isnan(x1)) {
		self->data[0] = x1;
		if (!c_isnan(x2) && !c_isnan(x3) && !c_isnan(x4)) {
			self->data[1] = x2;
			self->data[2] = x3;
			self->data[3] = x4;
		} else {
			if (!c_isnan(x2) || !c_isnan(x3) || !c_isnan(x4)) { errMsg("Invalid partial init of vec4"); }
			self->data[1] = self->data[2] = self->data[3] = x1;
		}
	} else {
		self->data[0] = self->data[1] = self->data[2] = self->data[3] = 0;
	}
	return 0;
}

static PyObject* PbVec4Repr(PbVec4* self) {
	Manta::Vec4 v(self->data[0], self->data[1], self->data[2], self->data[3]);
	return PyUnicode_FromFormat(v.toString().c_str());
}

PyTypeObject PbVec4Type = {
	PyVarObject_HEAD_INIT(NULL, 0)
	"manta.vec4",             /* tp_name */
	sizeof(PbVec4),             /* tp_basicsize */
	0,                         /* tp_itemsize */
	(destructor)PbVec4Dealloc, /* tp_dealloc */
	0,                         /* tp_print */
	0,                         /* tp_getattr */
	0,                         /* tp_setattr */
	0,                         /* tp_reserved */
	(reprfunc)PbVec4Repr,      /* tp_repr */
	NULL, // &PbVec4NumberMethods,      /* tp_as_number */
	0,                         /* tp_as_sequence */
	0,                         /* tp_as_mapping */
	0,                         /* tp_hash  */
	0,                         /* tp_call */
	0,                         /* tp_str */
	0,                         /* tp_getattro */
	0,                         /* tp_setattro */
	0,                         /* tp_as_buffer */
#if PY_MAJOR_VERSION >= 3
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE ,   /* tp_flags */
#else
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |  Py_TPFLAGS_CHECKTYPES,   /* tp_flags */
#endif
	"float vector type",       /* tp_doc */
	0,                     /* tp_traverse */
	0,                     /* tp_clear */
	0,                     /* tp_richcompare */
	0,                     /* tp_weaklistoffset */
	0,                     /* tp_iter */
	0,                     /* tp_iternext */
	PbVec4Methods,             /* tp_methods */
	PbVec4Members,             /* tp_members */
	0,                         /* tp_getset */
	0,                         /* tp_base */
	0,                         /* tp_dict */
	0,                         /* tp_descr_get */
	0,                         /* tp_descr_set */
	0,                         /* tp_dictoffset */
	(initproc)PbVec4Init,      /* tp_init */
	0,                         /* tp_alloc */
	PbVec4New,                 /* tp_new */
};

// register

void PbVecInitialize(PyObject* module) {
	if (PyType_Ready(&PbVec3Type) < 0) errMsg("can't initialize Vec3 type"); 
	Py_INCREF(castPy(&PbVec3Type));
	PyModule_AddObject(module, "vec3", (PyObject *)&PbVec3Type);

	if (PyType_Ready(&PbVec4Type) < 0) errMsg("can't initialize Vec4 type"); 
	Py_INCREF(castPy(&PbVec4Type));
	PyModule_AddObject(module, "vec4", (PyObject *)&PbVec4Type);
}
const static Pb::Register _REG(PbVecInitialize);

} // namespace
